/**
* @program: src
*
* @description:
*
* @author: Mr.chen
*
* @create: 2020-11-05 17:29
**/
package client

import (
	"ydd-im/service/api/define"
	"ydd-im/service/api/models"
	"ydd-im/service/api/services/wsservice/message"
	"ydd-im/service/api/tools/response"
	"ydd-im/service/api/tools/util"
	"ydd-im/util/logger"
	"fmt"
	"github.com/gorilla/websocket"
	"go.uber.org/zap"
	"strings"
	//"ydd-im/service/api/services/wsservice/client"
	"strconv"
	"time"
)

//channel通道
var ToClientChan chan clientInfo

//channel通道结构体
type clientInfo struct {
	ClientId     string
	SendUserId   string
	MessageId    string
	Code         int
	Msg          string
	MessageLogId int // 数据库消息ID
	Data   response.MessageData
}

type RetData struct {
	MessageId    string      `json:"messageId"`
	SendUserId   string      `json:"sendUserId"`
	Code         int         `json:"code"`
	Msg          string      `json:"msg"`
	Data         interface{} `json:"data"`
	MessageLogId int         `json:"messageLogId"`

}

// 心跳间隔
var heartbeatInterval = 25 * time.Second

func init() {
	ToClientChan = make(chan clientInfo, 1000)
}

var Manager = NewClientManager() // 管理者

//func StartWebSocket() {
//	websocketHandler := &wsconn.Controller{}
//	http.HandleFunc("/ws", websocketHandler.Conn)
//	go Manager.Start()
//}

//监听并发送给客户端信息
func WriteMessage() {
	for {
		clientInfo := <-ToClientChan
		logger.GetLg().Info("发送到本机",
			zap.String("host", define.LocalHost),
			zap.String("port", define.Port),
			zap.String("clientId", clientInfo.ClientId),
			zap.String("messageId", clientInfo.MessageId),
			zap.String("sendUserId", clientInfo.SendUserId),
			zap.Int("code", clientInfo.Code),
			zap.String("msg", clientInfo.Msg),
			zap.Int("messageLogId", clientInfo.Data.MessageLogId),
			zap.Int("ToUserId", clientInfo.Data.ToUserId),
			zap.Int("Media", clientInfo.Data.Media),
			zap.String("Pic", clientInfo.Data.Pic),
			zap.String("Url", clientInfo.Data.Url),
			zap.Int("MessageType", clientInfo.Data.MessageType),
			zap.String("SendTime", clientInfo.Data.SendTime),
		)
		if conn, err := Manager.GetByClientId(clientInfo.ClientId); err == nil && conn != nil {
			if err := Render(conn.Socket, clientInfo.MessageId, clientInfo.SendUserId, clientInfo.Code, clientInfo.Msg, clientInfo.Data, clientInfo.Data.MessageLogId); err != nil {
				Manager.DisConnect <- conn
				logger.GetLg().Error("客户端异常离线",
					zap.String("host", define.LocalHost),
					zap.String("port", define.Port),
					zap.String("clientId", clientInfo.ClientId),
					zap.String("msg", clientInfo.Msg),
					zap.Int("messageLogId", clientInfo.Data.MessageLogId),
					zap.Int("ToUserId", clientInfo.Data.ToUserId),
					zap.Int("Media", clientInfo.Data.Media),
					zap.String("Pic", clientInfo.Data.Pic),
					zap.String("Url", clientInfo.Data.Url),
					zap.Int("MessageType", clientInfo.Data.MessageType),
					zap.String("SendTime", clientInfo.Data.SendTime),
					zap.Any("err", err.Error()),
				)
			} else { // 发送成功，将消息到达表的记录删除

				err := message.UpdatemessageArrive(clientInfo.Data.MessageLogId, clientInfo.Data.ToUserId, 1)
				if err != nil {
					logger.GetLg().Error("更新消息到达记录表失败",
						zap.String("host", define.LocalHost),
						zap.String("port", define.Port),
						zap.String("clientId", clientInfo.ClientId),
						zap.String("msg", clientInfo.Msg),
						zap.Int("messageLogId", clientInfo.Data.MessageLogId),
						zap.Int("ToUserId", clientInfo.Data.ToUserId),
						zap.Int("Media", clientInfo.Data.Media),
						zap.String("Pic", clientInfo.Data.Pic),
						zap.String("Url", clientInfo.Data.Url),
						zap.Int("MessageType", clientInfo.Data.MessageType),
						zap.String("SendTime", clientInfo.Data.SendTime),
						zap.Any("err", err.Error()),
					)
				}
			}
		}
	}
}

func Render(conn *websocket.Conn, messageId string, sendUserId string, code int, message string, data interface{},messageLogId int) error {
	return conn.WriteJSON(RetData{
		Code:         code,
		MessageId:    messageId,
		SendUserId:   sendUserId,
		Msg:          message,
		Data:         data,
		MessageLogId: messageLogId,
	})
}

//启动定时器进行心跳检测，将客户端不在的去除掉，当用户上线了，也会检查，但时间过短会直接断开，不走这步操作
func PingTimer() {
	go func() {
		ticker := time.NewTicker(heartbeatInterval)
		defer ticker.Stop()
		for {
			<-ticker.C
			//发送心跳
			for clientId, conn := range Manager.AllClient() {
				if err := conn.Socket.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(time.Second)); err != nil {
					Manager.DisConnect <- conn
					logger.GetLg().Error(fmt.Sprintf("发送心跳失败: %s 总连接数：%d", clientId, Manager.Count()))
				}
			}
		}

	}()
}

//发送信息到指定分组
//func SendMessage2Group(systemId int, sendUserId, groupName string, code int, msg string, data *string) (messageId string) {
//	messageId = util.GenUUID()
//	if util.IsCluster() {
//		//发送分组消息给指定广播
//		go SendGroupBroadcast(strconv.Itoa(systemId), messageId, sendUserId, groupName, code, msg, data)
//	} else {
//		//如果是单机服务，则只发送到本机
//		Manager.SendMessage2LocalGroup(strconv.Itoa(systemId), messageId, sendUserId, groupName, code, msg, data)
//	}
//	return
//}

//通过本服务器发送信息
//func SendMessage2LocalClient(messageId, clientId string, sendUserId string, code int, msg string, data *string) {
//	logger.GetLg().Error("发送到通道",
//		zap.String("host", define.LocalHost),
//		zap.String("port", define.Port),
//		zap.String("clientId", clientId),
//	)
//	ToClientChan <- clientInfo{ClientId: clientId, MessageId: messageId, SendUserId: sendUserId, Code: code, Msg: msg, Data: data}
//	return
//}

//通过本服务器发送信息
func SendMessageLocalUser(messageId, clientId string, sendUserId string, code int, msg string,messageData response.MessageData ) {
	logger.GetLg().Error("发送到通道",
		zap.String("host", define.LocalHost),
		zap.String("port", define.Port),
		zap.String("clientId", clientId),
		zap.Int("messageLogId", messageData.MessageLogId),
		zap.Int("ToUserId", messageData.ToUserId),
		zap.Int("Media", messageData.Media),
		zap.String("Pic", messageData.Pic),
		zap.String("Url", messageData.Url),
		zap.Int("MessageType",messageData.MessageType),
		zap.String("SendTime", messageData.SendTime),
	)
	ToClientChan <- clientInfo{ClientId: clientId, MessageId: messageId, SendUserId: sendUserId, Code: code, Msg: msg, Data:messageData,MessageLogId:messageData.MessageLogId}
	return
}

//获取分组列表
func GetOnlineList(systemId *string, groupName *string) map[string]interface{} {
	var clientList []string
	if util.IsCluster() {
		//发送到系统广播
		clientList = GetOnlineListBroadcast(systemId, groupName)
	} else {
		//如果是单机服务，则只发送到本机
		retList := Manager.GetGroupClientList(util.GenGroupKey(*systemId, *groupName))
		clientList = append(clientList, retList...)
	}

	return map[string]interface{}{
		"count": len(clientList),
		"list":  clientList,
	}
}

//发送信息到指定客户端
//func SendMessageToClient(clientId string, sendUserId string, code int, msg string, data *string) (messageId string) {
//	messageId = util.GenUUID()
//	if util.IsCluster() {
//		addr, _, _, isLocal, err := util.GetAddrInfoAndIsLocal(clientId)
//		if err != nil {
//			logger.GetLg().Error("发送信息到指定客户端",
//				zap.String("host",define.LocalHost),
//				zap.String("port",define.Port),
//				zap.Any("err",err.Error()),
//			)
//			return
//		}
//		//如果是本机则发送到本机
//		if isLocal {
//			SendMessageLocalUser(messageId, clientId, sendUserId, code, msg, data,0)
//		} else {
//			//发送到指定机器
//			SendRpcUser(addr, messageId, sendUserId, clientId, code, msg, data,0)
//		}
//	} else {
//		//如果是单机服务，则只发送到本机
//		SendMessageLocalUser(messageId, clientId, sendUserId, code, msg, data,0)
//	}
//	return
//}
//发送信息到指定用户，在线的直接推送，不在线的是历史记录
//func SendMessageToUser(touserId int, sendUserId int, code int, msg string, data *string) (messageId string) {
//	messageId = util.GenUUID()
//	// 查询未过期的token，在线推送,包括发送者跟接受者token
//	modelUserToken := new(models.UserToken)
//	userTokenList,err := modelUserToken.GetUserTokenList(touserId,sendUserId)
//	if err != nil {
//		logger.GetLg().Error("获取发送与接收者信息失败",
//			zap.String("host",define.LocalHost),
//			zap.String("port",define.Port),
//			zap.Any("err",err.Error()),
//		)
//		return
//	}
//	if util.IsCluster() { // 如果是集群的
//	    for k,_ := range userTokenList { //循环各个客户端地址
//			addr, _, _, isLocal, err := util.GetAddrInfoAndIsLocal(userTokenList[k].ClientId)
//			if err != nil {
//				logger.GetLg().Error("发送信息到指定客户端",
//					zap.String("host",define.LocalHost),
//					zap.String("port",define.Port),
//					zap.Any("err",err.Error()),
//				)
//				return
//			}
//			//如果是本机则发送到本机
//			if isLocal {
//				SendMessageLocalUser(messageId, userTokenList[k].ClientId, strconv.Itoa(sendUserId) , code, msg, data)
//			} else {
//				//发送到指定机器
//				SendRpcUser(addr, messageId, strconv.Itoa(sendUserId), userTokenList[k].ClientId, code, msg, data)
//			}
//		}
//		//addr, _, _, isLocal, err := util.GetAddrInfoAndIsLocal(clientId)
//		//if err != nil {
//		//	logger.GetLg().Error("发送信息到指定客户端",
//		//		zap.String("host",define.LocalHost),
//		//		zap.String("port",define.Port),
//		//		zap.Any("err",err.Error()),
//		//	)
//		//	return
//		//}
//		////如果是本机则发送到本机
//		//if isLocal {
//		//	SendMessage2LocalClient(messageId, clientId, sendUserId, code, msg, data)
//		//} else {
//		//	//发送到指定机器
//		//	SendRpc2Client(addr, messageId, sendUserId, clientId, code, msg, data)
//		//}
//	} else {
//		//如果是单机服务，则只发送到本机
//		for k,_ := range userTokenList {
//			// 判断当前用户是否在线
//			SendMessageLocalUser(messageId, userTokenList[k].ClientId, strconv.Itoa(sendUserId) , code, msg, data)
//		}
//	}
//	return
//}
//发送信息到指定用户，在线的直接推送，包括用户自己
func SendMessageToUserAndMine(touserId int, sendUserId int, code int, msg string,messageData response.MessageData) (messageId string) {

	messageId = util.GenUUID()
	//判断是否被注册
	if util.IsCluster() { // 如果是集群的
		// 查询当前的客户端关系表
		modelUserClients := new(models.UserClients)
		userClientList, err := modelUserClients.GetUserToClientIdByTouidList(touserId)
		var deleteClientsData []string
		if err != nil {
			logger.GetLg().Error("获取发送与接收者信息失败",
				zap.String("host", define.LocalHost),
				zap.String("port", define.Port),
				zap.Int("messageLogId", messageData.MessageLogId),
				zap.Any("err", err.Error()),
			)
			return
		}
		for k, _ := range userClientList { //循环各个客户端地址
			addr, _, _, isLocal, err := util.GetAddrInfoAndIsLocal(userClientList[k].ClientId)

			if err != nil {
				logger.GetLg().Error("发送信息到指定客户端",
					zap.String("host", define.LocalHost),
					zap.String("port", define.Port),
					zap.String("clientId", userClientList[k].ClientId),
					zap.Int("messageLogId", messageData.MessageLogId),
					zap.Any("err", err.Error()),
				)
				return
			}
			// 判断地址是否在服务器内，不在的过滤掉，以免等太久
			if _,ok := define.ServerList[define.ETCD_SERVER_LIST+addr];!ok{// 结束
				continue;
			}
			//fmt.Println("==================",userClientList[k].ClientId,isLocal,addr)
			//如果是本机则发送到本机
			if isLocal {
				if _, err := Manager.GetByClientId(userClientList[k].ClientId); err == nil {
					//如果是单机，
					SendMessageLocalUser(messageId, userClientList[k].ClientId, strconv.Itoa(sendUserId), code, msg,messageData)
				} else {
					// 将 不在线的客户端删除
					deleteClientsData = append(deleteClientsData, strconv.Itoa(userClientList[k].Id))
					logger.GetLg().Error("发送信息到指定客户端失败，客户端不在线",
						zap.String("host", define.LocalHost),
						zap.String("port", define.Port),
						zap.String("clientId", userClientList[k].ClientId),
						zap.Int("messageLogId", messageData.MessageLogId),
						zap.Any("err", err.Error()),
					)
				}
			} else {
				//发送到指定机器
				SendRpcUser(addr, messageId, strconv.Itoa(sendUserId), userClientList[k].ClientId, code, msg,messageData)
			}
		}
		// 删除多余的链接
		if len(deleteClientsData) > 0 {
			wherestr := strings.Join(deleteClientsData, ",")
			err = modelUserClients.DeleteUserToClients(wherestr)
			if err != nil {
				logger.GetLg().Error("删除指定客户端失败",
					zap.String("host", define.LocalHost),
					zap.String("port", define.Port),
					zap.String("clientIds", wherestr),
					zap.Int("messageLogId", messageData.MessageLogId),
					zap.Any("err", err.Error()),
				)
				return
			}
		}
	} else {
		//如果是单机服务，则只发送到本机
		userClientList := Manager.GetSystemClientList(touserId)
		for k, _ := range userClientList {
			// 判断当前用户是否在线
			if _, err := Manager.GetByClientId(userClientList[k]); err == nil {
				//如果是单机，就直接添加到本地group了
				SendMessageLocalUser(messageId, userClientList[k], strconv.Itoa(sendUserId), code, msg, messageData)
			} else {
				logger.GetLg().Error("发送信息到指定客户端失败，客户端不在线",
					zap.String("host", define.LocalHost),
					zap.String("port", define.Port),
					zap.String("clientId", userClientList[k]),
					zap.Int("messageLogId", messageData.MessageLogId),
					zap.Any("err", err.Error()),
				)
			}
		}
	}
	return
}

////发送信息到指定用户，在线的直接推送，包括用户自己
//func SendMessageToUserAndMine(touserId int, sendUserId int, code int, msg string, data *string) (messageId string) {
//	messageId = util.GenUUID()
//	// 查询未过期的token，在线推送,包括发送者跟接受者token
//	modelUserToken := new(models.UserToken)
//	userTokenList,err := modelUserToken.GetUserTokenByTouidList(touserId)
//	if err != nil {
//		logger.GetLg().Error("获取发送与接收者信息失败",
//			zap.String("host",define.LocalHost),
//			zap.String("port",define.Port),
//			zap.Any("err",err.Error()),
//		)
//		return
//	}
//
//	if util.IsCluster() { // 如果是集群的
//		for k,_ := range userTokenList { //循环各个客户端地址
//			addr, _, _, isLocal, err := util.GetAddrInfoAndIsLocal(userTokenList[k].ClientId)
//			if err != nil {
//				logger.GetLg().Error("发送信息到指定客户端",
//					zap.String("host",define.LocalHost),
//					zap.String("port",define.Port),
//					zap.Any("err",err.Error()),
//				)
//				return
//			}
//			//如果是本机则发送到本机
//			if isLocal {
//				if _, err := Manager.GetByClientId(userTokenList[k].ClientId); err == nil {
//					//如果是单机，
//					SendMessageLocalUser(messageId, userTokenList[k].ClientId, strconv.Itoa(sendUserId) , code, msg, data)
//				}
//
//			} else {
//				//发送到指定机器
//				SendRpcUser(addr, messageId, strconv.Itoa(sendUserId), userTokenList[k].ClientId, code, msg, data)
//			}
//		}
//
//	} else {
//		//如果是单机服务，则只发送到本机
//		for k,_ := range userTokenList {
//			// 判断当前用户是否在线
//			if _, err := Manager.GetByClientId(userTokenList[k].ClientId); err == nil {
//				//如果是单机，就直接添加到本地group了
//				SendMessageLocalUser(messageId, userTokenList[k].ClientId, strconv.Itoa(sendUserId) , code, msg, data)
//			}
//
//		}
//	}
//	return
//}

// 下线操作，发送信息到指定用户
//func SendMessageToUserClose(clientId string, sendUserId int, code int, msg string, data *string) (messageId string) {
//	messageId = util.GenUUID()
//	// 查询未过期的token，在线推送,包括发送者跟接受者token
//
//	if util.IsCluster() { // 如果是集群的
//
//			addr, _, _, isLocal, err := util.GetAddrInfoAndIsLocal(clientId)
//			if err != nil {
//				logger.GetLg().Error("发送信息到指定客户端",
//					zap.String("host",define.LocalHost),
//					zap.String("port",define.Port),
//					zap.Any("err",err.Error()),
//				)
//				return
//			}
//			//如果是本机则发送到本机
//			if isLocal {
//				SendMessageLocalUser(messageId, clientId, strconv.Itoa(sendUserId) , code, msg, data)
//			} else {
//				//发送到指定机器
//				SendRpcUser(addr, messageId, strconv.Itoa(sendUserId), clientId, code, msg, data)
//			}
//
//	} else {
//		//如果是单机服务，则只发送到本机
//		SendMessageLocalUser(messageId, clientId, strconv.Itoa(sendUserId) , code, msg, data)
//	}
//	return
//}

//添加用户到分组,groupid群ID,userid 用户id
func AddUserToGroup(groupid int, userid int) {
	//集群
	if util.IsCluster() {
		//判断key是否存在
		//addr, _, _, isLocal, err := util.GetAddrInfoAndIsLocal(clientId)
		//if err != nil {
		//	logger.GetLg().Error("发送信息到指定客户端",
		//		zap.String("host",define.LocalHost),
		//		zap.String("port",define.Port),
		//		zap.Any("err",err.Error()),
		//	)
		//	return
		//}
		//
		//if isLocal {
		//	if client, err := Manager.GetByClientId(clientId); err == nil {
		//		//添加到本地
		//		Manager.AddClient2LocalGroup(groupName, client, userId, extend)
		//	} else {
		//		//log.Error(err)
		//	}
		//} else {
		//	//发送到指定的机器
		//	SendRpcBindGroup(addr, systemId, groupName, clientId, userId, extend)
		//}
	} else {
		//if client, err := Manager.GetByClientId(clientId); err == nil {
		//	//如果是单机，就直接添加到本地group了
		//	Manager.AddClient2LocalGroup(groupName, client, userId, extend)
		//}
		//client.AddClient2LocalGroup(groupName, client, userId, extend)
	}
}
